#include "dataview.h"
#include "qhexedit2/qhexedit.h"
#include "protocol.h"
#include "udfmodel.h"
#include "pdxpmodel.h"
#include "framemodel.h"
#include "fixedmodel.h"
#include "diutils.h"

#include <QSplitter>
#include <QVBoxLayout>
#include <QTableView>
#include <QHeaderView>
#include <QListView>
#include <QInputDialog>
#include <QKeyEvent>
#include <QFileDialog>
#include <QMenu>
#include <QProgressDialog>
#include <QMessageBox>

#define BUFFER_SIZE 0x10000

DataView::DataView(QWidget *parent)
{
    Q_UNUSED(parent);
    init();
}

DataView::DataView(QString file, QWidget *parent, bool temp)
{
    Q_UNUSED(parent);
    init();
    tempFile = temp;
    openFile(file);
}

bool DataView::openFile(QString name)
{
    file.setFileName(name);
    hexEdit->setData(file);
    return file.isReadable();
}

const QFile& DataView::getFile()
{
    return file;
}

void DataView::init()
{
    createActions();

    QHBoxLayout* layout = new QHBoxLayout(this);
    layout->setMargin(0);
    layout->setSpacing(0);
    vSplitter = new QSplitter(Qt::Vertical, this);

    hexEdit = new QHexEdit(this);
    connect(hexEdit, SIGNAL(currentAddressChanged(qint64)), SLOT(addressChanged(qint64)));
    vSplitter->addWidget(hexEdit);

    recordView = new QTableView;
    HexEditSettingchaged();

    recordView->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(recordView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(tableContextMenuRequested(QPoint)));

    recordView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
//    recordView->horizontalHeader()->setMinimumWidth(120);
//    recordView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
    recordView->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(recordView->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)),
            this, SLOT(horizontalHeaderMenuRequested(QPoint)));

    recordView->verticalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(recordView->verticalHeader(), SIGNAL(customContextMenuRequested(QPoint)),
            this, SLOT(verticalHeaderMenuRequested(QPoint)));

    recordView->setShowGrid(false);
    recordView->setAlternatingRowColors(true);
//    recordView->installEventFilter(this);
    connect(recordView, SIGNAL(clicked(QModelIndex)), SLOT(recordClicked(QModelIndex)));
    vSplitter->addWidget(recordView);
    recordView->hide();

    layout->addWidget(vSplitter);
    this->setLayout(layout);

    adjust(Hex);
}

void DataView::createActions()
{
    deleteAction = new QAction(tr("&Delete"), this);
    deleteAction->setShortcut(QKeySequence::Delete);
    connect(deleteAction, SIGNAL(triggered()), this, SLOT(tableDeleted()));

    deleteColumnAction = new QAction(tr("Delete current column"), this);
    connect(deleteColumnAction, SIGNAL(triggered()), this, SLOT(deleteColumn()));

    deleteRowAction = new QAction(tr("Delete current row"), this);
    connect(deleteRowAction, SIGNAL(triggered()), this, SLOT(deleteRow()));

    saveAsAction = new QAction(tr("&Save as..."), this);
    saveAsAction->setShortcut(QKeySequence::SaveAs);
    connect(saveAsAction, SIGNAL(triggered()), this, SLOT(tableSaveAs()));

    filterAction = new QAction(tr("&Filter..."), this);
    connect(filterAction, SIGNAL(triggered()), this, SLOT(tableFilter()));

    filterColumnAction = new QAction(tr("Filter with current column"), this);
    connect(filterColumnAction, SIGNAL(triggered()), this, SLOT(columnFilter()));
}

QString DataView::getSaveFileName()
{
    return QFileDialog::getSaveFileName(this,
                                        tr("Save File"),
                                        file.fileName() + ("_"),
                                        tr("All File (*)"));
}

void DataView::deleteRows(const QModelIndexList &rows)
{
    QString fileName = getSaveFileName();
    if (!fileName.isEmpty()) {
        ProtocolTableModel* ptModel = dynamic_cast<ProtocolTableModel*>(recordView->model());
        ptModel->deleteRows(fileName, rows);
    }
}

void DataView::deleteColumns(const QModelIndexList &cols)
{
    QString fileName = getSaveFileName();
    if (!fileName.isEmpty()) {
        ProtocolTableModel* ptModel = dynamic_cast<ProtocolTableModel*>(recordView->model());
        ptModel->deleteColumns(fileName, cols);
    }
}

void DataView::addressChanged(qint64 address)
{
    emit viewAddressChanged(this, address);

    if (recordView->isHidden())
        return;

    if (ProtocolTableModel* model = dynamic_cast<ProtocolTableModel*>(recordView->model())) {
        QModelIndex selectIndex = recordView->currentIndex();
        QModelIndex index = model->locate(address);

        if (index.row() == selectIndex.row()) {
            if (index.column() == selectIndex.column())
                return;

            if (index.column() == 1 && selectIndex.column() == 0)
                return;
        }

        recordView->scrollTo(index);
        recordView->selectionModel()->select(index, QItemSelectionModel::SelectCurrent);
        recordView->setFocus();

    }
}

void DataView::recordClicked(const QModelIndex &index)
{
    if (ProtocolTableModel* model = dynamic_cast<ProtocolTableModel*>(recordView->model())) {
        qint64 pos = model->position(index) * 2;
        hexEdit->setCursorPosition(pos);
//        hexEdit->setFocus();
        hexEdit->ensureVisible();
    }
}

void DataView::columnsInserted(const QModelIndex &parent, int first, int last)
{
    Q_UNUSED(parent);

    if (Fixed == viewMode || Frame == viewMode) {
        for (int col = first; col <= last + 1; col++) {
            if (0 == col)
                recordView->setColumnWidth(0, 64);
            else
                recordView->setColumnWidth(col, 32);
        }
    }
}

void DataView::rowsInserted(const QModelIndex &parent, int first, int last)
{
    Q_UNUSED(parent);
    Q_UNUSED(last);

    if (Fixed != viewMode && Frame != viewMode && first == 0)
        recordView->resizeColumnsToContents();
}

void DataView::tableDeleted()
{
    QItemSelectionModel* model = recordView->selectionModel();
    ProtocolTableModel* ptModel = dynamic_cast<ProtocolTableModel*>(recordView->model());
    QModelIndexList rows = model->selectedRows();
    QModelIndexList cols = model->selectedColumns();
    if (rows.size() > 0) {
        QString fileName = getSaveFileName();
        if (!fileName.isEmpty())
            ptModel->deleteRows(fileName, rows);
    } else if (cols.size() > 0) {
        QString fileName = getSaveFileName();
        if (!fileName.isEmpty())
            ptModel->deleteColumns(fileName, cols);
    }
}

void DataView::tableSaveAs()
{
    QItemSelectionModel* model = recordView->selectionModel();
    ProtocolTableModel* ptModel = dynamic_cast<ProtocolTableModel*>(recordView->model());
    QModelIndexList rows = model->selectedRows();
    QModelIndexList cols = model->selectedColumns();
    if (rows.size() > 0) {
        QString fileName = getSaveFileName();
        if (!fileName.isEmpty())
            ptModel->saveRows(fileName, rows);
    } else if (cols.size() > 0) {
        QString fileName = getSaveFileName();
        if (!fileName.isEmpty())
            ptModel->saveColumns(fileName, cols);
    }
}

void DataView::tableFilter()
{
    QModelIndex index = recordView->currentIndex();
    ProtocolTableModel* model = dynamic_cast<ProtocolTableModel*>(recordView->model());
    model->filter(index);
}

void DataView::columnFilter()
{
    QModelIndex index = filterColumnAction->data().toModelIndex();
    ProtocolTableModel* model = dynamic_cast<ProtocolTableModel*>(recordView->model());
    model->filter(index);
}

void DataView::horizontalHeaderMenuRequested(const QPoint &pos)
{
    int col = recordView->horizontalHeader()->logicalIndexAt(pos);
    QModelIndex index = recordView->model()->index(0, col);
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);
    deleteColumnAction->setData(index);
    if (index.column()!=0)
        menu->addAction(deleteColumnAction);
    ProtocolTableModel* ptModel = dynamic_cast<ProtocolTableModel*>(recordView->model());
    if (ptModel->supportFilter(index)) {
        filterColumnAction->setData(index);
        menu->addAction(filterColumnAction);
    }

    menu->popup(recordView->horizontalHeader()->viewport()->mapToGlobal(pos));
}

void DataView::verticalHeaderMenuRequested(const QPoint &pos)
{
    QModelIndex index = recordView->indexAt(pos);
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);
    deleteRowAction->setData(index);
    menu->addAction(deleteRowAction);

    menu->popup(recordView->verticalHeader()->viewport()->mapToGlobal(pos));
}

void DataView::tableContextMenuRequested(const QPoint &pos)
{
    QModelIndex index = recordView->indexAt(pos);
    QMenu *menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);

    if (!recordView->selectionModel()->selectedRows().isEmpty() ||
            !recordView->selectionModel()->selectedColumns().isEmpty()) {
        menu->addAction(deleteAction);
        menu->addAction(saveAsAction);
    }
    ProtocolTableModel* ptModel = dynamic_cast<ProtocolTableModel*>(recordView->model());
    if (ptModel->supportFilter(index)) {
        filterAction->setData(index);
        menu->addAction(filterAction);
    }
    menu->popup(recordView->viewport()->mapToGlobal(pos));
}

void DataView::deleteRow()
{
    QModelIndex index = deleteRowAction->data().toModelIndex();
    QModelIndexList list;
    list.append(index);
    deleteRows(list);
}

void DataView::deleteColumn()
{
    QModelIndex index = deleteColumnAction->data().toModelIndex();
    QModelIndexList list;
    list.append(index);
    deleteColumns(list);
}

bool DataView::eventFilter(QObject *watched, QEvent *event)
{
    if (watched != recordView)
        return false;

    QEvent::Type eventType = event->type();
    if (QEvent::KeyPress == eventType) {
        QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>(event);
        int key = keyEvent->key();
        if (key == Qt::Key_Delete) {
            QItemSelectionModel* model = recordView->selectionModel();
            ProtocolTableModel* ptModel = dynamic_cast<ProtocolTableModel*>(recordView->model());
            QModelIndexList rows = model->selectedRows();
            QModelIndexList cols = model->selectedColumns();
            if (rows.size() > 0) {
                QString fileName = getSaveFileName();
                if (!fileName.isEmpty())
                    ptModel->deleteRows(fileName, rows);
            } else if (cols.size() > 0) {
                QString fileName = getSaveFileName();
                if (!fileName.isEmpty())
                    ptModel->deleteColumns(fileName, cols);
            }
        }
    }/* else if (QEvent::ContextMenu == eventType) {
        QMenu* popup = new QMenu(this);
    }*/

    return false;
}

qint64 DataView::cursorPosition(void)
{
    return hexEdit->cursorPosition();
}

QByteArray DataView::dataAt(qint64 pos, qint64 count)
{
    return hexEdit->dataAt(pos, count);
}

void DataView::indexOf(const QByteArray &ba, qint64 from)
{
    qint64 pos = forwardFind(file, ba, from + ba.size());
    if (pos > -1) {
//        hexEdit->setCursorPosition(pos * 2);
//        hexEdit->ensureVisible();
        hexEdit->indexOf(ba, pos - 1);
    }
}

void DataView::lastIndexOf(const QByteArray &ba, qint64 from)
{
    qint64 pos = backwardFind(file, ba, from);
    if (pos > -1) {
//        hexEdit->setCursorPosition(pos * 2);
//        hexEdit->ensureVisible();
        hexEdit->lastIndexOf(ba, pos - 1);
    }
}

void DataView::toggleHexView()
{
    adjust(Hex);
}

void DataView::toggleFixedView()
{
    QInputDialog input;
    input.setInputMode(QInputDialog::IntInput);
    input.setWindowTitle(tr("Input"));
    input.setLabelText(tr("Frame Size:"));
    input.setIntMinimum(0);
    input.setIntMaximum(2048);
    input.setOkButtonText(tr("OK"));
    input.setCancelButtonText(tr("Cancel"));

    if (QDialog::Accepted == input.exec()) {
        int size = input.intValue();
        if (Fixed == viewMode) {
            FixedModel* model = dynamic_cast<FixedModel*>(recordView->model());
            if (model && model->frameSize() == size)
                return;
        }

        adjust(Fixed);
        FixedModel* model = new FixedModel(file.fileName(), recordView);
        QAbstractItemModel* oldModel = recordView->model();
        recordView->setModel(model);
        delete oldModel;
        connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)),
                SLOT(rowsInserted(QModelIndex, int, int)));
        connect(model, SIGNAL(columnsInserted(QModelIndex, int, int)),
                SLOT(columnsInserted(QModelIndex, int, int)));
        model->setFrameSize(input.intValue());
    }
}

void DataView::toggleUDFView(void)
{
    if (viewMode != UDF) {
        UDFModel* model = new UDFModel(file.fileName(), recordView);
        QAbstractItemModel* oldModel = recordView->model();
        recordView->setModel(model);
        connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
                SLOT(rowsInserted(QModelIndex,int,int)));
        connect(model, SIGNAL(openTempFile(QString)), this, SIGNAL(openTempFile(QString)));
        delete oldModel;
        adjust(UDF);
    }
}

void DataView::togglePDXPView(void)
{
    if (viewMode != PDXP) {
        PDXPModel* model = new PDXPModel(file.fileName(), recordView);
        QAbstractItemModel* oldModel = recordView->model();
        recordView->setModel(model);
        connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
                SLOT(rowsInserted(QModelIndex,int,int)));
        connect(model, SIGNAL(openTempFile(QString)), this, SIGNAL(openTempFile(QString)));
        delete oldModel;
        adjust(PDXP);
    }
}

void DataView::toggleFrameView(void)
{
    bool ok;
    QInputDialog input;
    input.setInputMode(QInputDialog::TextInput);
    input.setWindowTitle(tr("Input"));
    input.setLabelText(tr("Condition Code:"));
    input.setOkButtonText(tr("OK"));
    input.setCancelButtonText(tr("Cancel"));
    if(input.exec()==QDialog::Accepted){
       ok=true;
    }else{
       ok=false;
    }

    QString text = input.textValue();
    if (ok && !text.isEmpty()) {
        QByteArray ba = QByteArray::fromHex(text.toLatin1());
        FrameModel* model = new FrameModel(file.fileName(), ba, recordView);
        QAbstractItemModel* oldModel = recordView->model();
        recordView->setModel(model);
        connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
                SLOT(rowsInserted(QModelIndex,int,int)));
        connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
                SLOT(columnsInserted(QModelIndex,int,int)));
        connect(model, SIGNAL(openTempFile(QString)), this, SIGNAL(openTempFile(QString)));
        delete oldModel;
    }

    if (viewMode != Frame)
        adjust(Frame);
}

void DataView::adjust(ViewMode mode)
{
    if (Hex == mode) {
        hexEdit->setReadOnly(false);
        recordView->hide();
    } else {
        hexEdit->setReadOnly(true);
        recordView->show();
    }
    viewMode = mode;
}

void DataView::HexEditSettingchaged()
{
    QSettings settings;

#ifdef Q_OS_WIN32
    hexEdit->setFont(settings.value("WidgetFont", QFont("Courier", 10)).value<QFont>());
    recordView->setFont(settings.value("ViewFont", QFont("Courier", 10)).value<QFont>());
#else
    hexEdit->setFont(settings.value("WidgetFont", QFont("Monospace", 10)).value<QFont>());
    recordView->setFont(settings.value("ViewFont", QFont("Monospace", 10)).value<QFont>());
#endif

    hexEdit->setAddressAreaColor(settings.value("AddressAreaColor", this->palette().alternateBase().color()).value<QColor>());
    hexEdit->setHighlightingColor(settings.value("HighlightingColor", QColor(0xff, 0xff, 0x99, 0xff)).value<QColor>());
    hexEdit->setSelectionColor(settings.value("SelectionColor", this->palette().highlight().color()).value<QColor>());
    hexEdit->setAddressFontColor(settings.value("AddressFontColor", QPalette::WindowText).value<QColor>());
    hexEdit->setAsciiAreaColor(settings.value("AsciiAreaColor", this->palette().alternateBase().color()).value<QColor>());
    hexEdit->setAsciiFontColor(settings.value("AsciiFontColor", QPalette::WindowText).value<QColor>());
    hexEdit->setHexFontColor(settings.value("HexFontColor", QPalette::WindowText).value<QColor>());
    hexEdit->adjustSize();
}

bool DataView::HexEditisModified()
{
    return hexEdit->isModified();
}

bool DataView::IstempFile()
{
    return tempFile;
}

void DataView::savaAs()
{
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"),
                                                    file.fileName() + "_");
    if (fileName.isEmpty())
        return;

    QApplication::setOverrideCursor(Qt::WaitCursor);
    QFile outFile(fileName);
    bool ok = hexEdit->write(outFile);
    QApplication::restoreOverrideCursor();

    if (!ok) {
        QMessageBox::warning(this, tr("Save As"),
                             tr("Cannot write file %1.")
                             .arg(fileName));
    }
}

void DataView::redo()
{
    hexEdit->redo();
}

void DataView::undo()
{
    hexEdit->undo();
}

void DataView::deletetempFile()
{
    if (tempFile)
        file.remove();
}
